package com.cskaoyan.javase.thread._4synchronized._2lock;

import com.sun.org.apache.bcel.internal.generic.NEW;

import java.util.concurrent.locks.ReentrantLock;

/**
 * Lock同步锁指的是，基于java.util.concurrent.Lock接口的实现类所实现的锁，它更多灵活，功能更强大。
 * 一般常用的实现类ReentrantLock,它表示一个可重进入的锁
 * 可重入锁的概念:
 *      当一个线程获取并释放锁以后,这个线程可以再次获取锁
 *      这种锁就是可重入锁
 * synchronized锁对象,也是一个可重入锁
 *
 * 构造器一般使用无参构造创建就可以了,和synchronized锁对象一样,要保证多个线程使用的锁是同一把锁才能够实现线程同步
 * 普通定义全局常量ReentrantLock锁对象
 *
 * 该锁对象提供了诸如获取锁,释放锁,判断锁等一系列操作的成员方法:
 * lock()	void	加锁、获取锁
 * unlock()	void	释放锁
 * boolean isHeldByCurrentThread() 查询当前线程是否保持此锁。
 *      该方法返回true,表示线程已经拿到锁了,否则表示还未获取锁
 * lock获取锁,unlock释放锁,往往都是成对出现的,它们之间的代码就是一个原子操作
 *
 * 注意事项:
 * 为保证锁对象能正常释放，将unlock()方法的调用放入finally代码块是最好的选择。
 *
 * @since 17:00
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public static void main(String[] args) {
        SellTicketTask task = new SellTicketTask();
        Thread t1 = new Thread(task, "窗口1");
        Thread t2 = new Thread(task, "窗口2");
        Thread t3 = new Thread(task, "窗口3");
        t1.start();
        t2.start();
        t3.start();
    }
}

class SellTicketTask implements Runnable {
    private int ticketNum = 1;
    private static final ReentrantLock LOCK = new ReentrantLock();

    @Override
    public void run() {
        // 使用同步代码块保证多线程环境下的数据安全
        while (ticketNum <= 100) {
            if (!LOCK.isHeldByCurrentThread()) {
                // 还没有获取锁
                System.out.println(Thread.currentThread().getName() + "已经在执行了,但是还未获取锁对象,无法执行售票任务!继续等待获取锁!");
            }
            // 加锁,进入一个原子操作
            LOCK.lock();
            System.out.println(Thread.currentThread().getName() + "已经获取了锁对象,开始执行售票任务!");
            if (ticketNum > 100) {
                // 判断此时的票数已经超卖,立刻释放锁,结束方法
                System.out.println(Thread.currentThread().getName() + "发现票已经卖完了,不再卖票了,售票结束!释放锁对象!");
                LOCK.unlock();
                return;
            }
            // 每次卖出一张票,休眠一段时间,模拟卖票需要耗费一点时间

            System.out.println(Thread.currentThread().getName() + "卖出了第" + ticketNum + "张票!");
            ticketNum++;
            System.out.println(Thread.currentThread().getName() + "执行了一次卖票任务,释放锁!");
            // LOCK.unlock();
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                // 为了确保稳定释放锁,把它放入finally代码块中执行!
                LOCK.unlock();
            }
        }

    }
}